package cn.ecnu.controller;

import cn.ecnu.pojo.Favorite;
import cn.ecnu.pojo.Journal;
import cn.ecnu.service.FavoriteService;
import cn.ecnu.service.JournalService;
import cn.ecnu.utils.Result;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/journals")
@Api(tags = "Journal Management APIs", description = "APIs for managing journals")
public class JournalController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JournalService journalService;

    @Autowired
    private FavoriteService favoriteService;

    String redisViewKey = "top10_journal_views";
    String redisFollowKey = "top10_journal_follows";

    @ApiOperation(value = "Create a new journal", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully created journal"),
            @ApiResponse(code = 400, message = "Bad Request")
    })
    @PostMapping
    public Result<Journal> createJournal(@ApiParam(value = "Journal object to be created", required = true) @RequestBody Journal journal) {
        boolean saved = journalService.save(journal);
        if (saved) {
            return Result.ok("Successfully created journal", journal);
        } else {
            return Result.error("Failed to create journal");
        }
    }

    @ApiOperation(value = "Get all journals", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved list"),
            @ApiResponse(code = 400, message = "Bad Request")
    })
    @GetMapping
    public Result<List<Journal>> getAllJournals() {
        List<Journal> journals = journalService.list();
        return Result.ok("Successfully retrieved list", journals);
    }

    @ApiOperation(value = "Get journals with pagination", response = Result.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "Page number", required = true, dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", value = "Number of items per page", required = true, dataType = "int", paramType = "query")
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved paginated list"),
            @ApiResponse(code = 400, message = "Bad Request")
    })
    @GetMapping("/page")
    public Result<Page<Journal>> getJournalsPage(@RequestParam int page, @RequestParam int size) {
        Page<Journal> journalPage = new Page<>(page, size);
        Page<Journal> resultPage = journalService.page(journalPage);
        return Result.ok("Successfully retrieved paginated list", resultPage);
    }

    @ApiOperation(value = "Update a journal by ID", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully updated journal"),
            @ApiResponse(code = 404, message = "Journal not found")
    })
    @PutMapping("/{id}")
    public Result<Journal> updateJournal(
            @ApiParam(value = "ID of the journal to be updated", required = true) @PathVariable Long id,
            @ApiParam(value = "Updated journal object", required = true) @RequestBody Journal journal) {
        journal.setJournalId(id);
        boolean updated = journalService.updateById(journal);
        if (updated) {
            return Result.ok("Successfully updated journal", journal);
        } else {
            return Result.error("Failed to update journal");
        }
    }

    @ApiOperation(value = "Delete a journal by ID", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully deleted journal"),
            @ApiResponse(code = 404, message = "Journal not found")
    })
    @DeleteMapping("/{id}")
    public Result<String> deleteJournal(@ApiParam(value = "ID of the journal to be deleted", required = true) @PathVariable Long id) {
        boolean removed = journalService.removeById(id);
        if (removed) {
            return Result.ok("Successfully deleted journal");
        } else {
            return Result.error("Failed to delete journal");
        }
    }

    @ApiOperation(value = "Search journals by keyword", response = Result.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "keyword", value = "Keyword to search in title or description", required = true, dataType = "String", paramType = "query")
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved list of journals"),
            @ApiResponse(code = 400, message = "Bad Request")
    })
    @GetMapping("/search")
    public Result<List<Journal>> searchJournals(HttpServletRequest request) {
        QueryWrapper<Journal> queryWrapper = new QueryWrapper<>();
        String keyword = request.getParameter("keyword");
        queryWrapper.apply("LOWER(title) LIKE {0} OR LOWER(description) LIKE {0} OR LOWER(short_name) LIKE {0}",
                "%" + keyword.toLowerCase() + "%");
        List<Journal> journals = journalService.list(queryWrapper);
        return Result.ok("Successfully retrieved list of journals", journals);
    }

    @ApiOperation(value = "Get favorite journals by user ID", response = Result.class)
    @ApiResponses({
            @ApiResponse(code = 200, message = "Successfully retrieved favorite journals"),
            @ApiResponse(code = 404, message = "User not found or no favorite journals")
    })
    @GetMapping("/favorites/{userId}")
    public Result<List<Journal>> getFavoriteJournalsByUserId(@ApiParam(value = "ID of the user to get favorite journals", required = true) @PathVariable Long userId) {
        QueryWrapper<Favorite> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).isNotNull("journal_id");
        List<Favorite> favorites = favoriteService.list(queryWrapper);
        List<Long> journalIds = favorites.stream().map(Favorite::getJournalId).collect(Collectors.toList());
        if (journalIds.isEmpty()) {
            return Result.ok("No favorite journals found", Collections.emptyList());
        }
        List<Journal> journals = journalService.listByIds(journalIds);
        return Result.ok("Successfully retrieved favorite journals", journals);
    }

    @ApiOperation(value = "Get top 10 journals by views", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved top 10 journals by views"),
            @ApiResponse(code = 500, message = "Internal Server Error")
    })
    @GetMapping("/top10/views")
    public Result<List<Journal>> getTop10JournalsByViews() {
        List<Journal> journals = (List<Journal>) redisTemplate.opsForValue().get(redisViewKey);
        if (journals == null) {
            journals = journalService.getTop10JournalsByViews();
            redisTemplate.opsForValue().set(redisViewKey, journals);
            redisTemplate.expire(redisViewKey, 30, TimeUnit.MINUTES);
        }
        return Result.ok("Successfully retrieved top 10 journals by views", journals);
    }

    @ApiOperation(value = "Get top 10 journals by follows", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved top 10 journals by follows"),
            @ApiResponse(code = 500, message = "Internal Server Error")
    })
    @GetMapping("/top10/follows")
    public Result<List<Journal>> getTop10JournalsByFollows() {
        List<Journal> journals = (List<Journal>) redisTemplate.opsForValue().get(redisFollowKey);
        if (journals == null) {
            journals = journalService.getTop10JournalsByFollows();
            redisTemplate.opsForValue().set(redisFollowKey, journals);
            redisTemplate.expire(redisFollowKey, 30, TimeUnit.MINUTES);
        }
        return Result.ok("Successfully retrieved top 10 journals by follows", journals);
    }

    @ApiOperation(value = "Get a journal by ID and increment views", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved journal"),
            @ApiResponse(code = 404, message = "Journal not found")
    })
    @GetMapping("/{id}")
    public Result<Journal> getJournalById(@ApiParam(value = "ID of the journal to get", required = true) @PathVariable Long id) {
        journalService.incrementViews(id); // Increment views
        Journal journal = journalService.getById(id);
        if (journal != null) {
            return Result.ok("Successfully retrieved journal", journal);
        } else {
            return Result.error("Journal not found");
        }
    }

    @ApiOperation(value = "Follow a journal and increment its follow count", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully followed journal"),
            @ApiResponse(code = 404, message = "Journal not found")
    })
    @PostMapping("/follow/{userId}/{journalId}")
    public Result<String> followJournal(@ApiParam(value = "ID of the user who is following", required = true) @PathVariable Long userId,
                                        @ApiParam(value = "ID of the journal to follow", required = true) @PathVariable Long journalId) {
        Favorite favorite = new Favorite();
        favorite.setUserId(userId);
        favorite.setJournalId(journalId);
        favoriteService.save(favorite);
        journalService.incrementFollows(journalId);
        return Result.ok("Successfully followed journal");
    }

    @ApiOperation(value = "Unfollow a journal and decrement its follow count", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully unfollowed journal"),
            @ApiResponse(code = 404, message = "Journal not found")
    })
    @DeleteMapping("/unfollow/{userId}/{journalId}")
    public Result<String> unfollowJournal(@ApiParam(value = "ID of the user who is unfollowing", required = true) @PathVariable Long userId,
                                          @ApiParam(value = "ID of the journal to unfollow", required = true) @PathVariable Long journalId) {
        QueryWrapper<Favorite> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).eq("journal_id", journalId);
        favoriteService.remove(queryWrapper);
        journalService.decrementFollows(journalId);
        return Result.ok("Successfully unfollowed journal");
    }

    @ApiOperation(value = "Get journals followed by a user", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved followed journals"),
            @ApiResponse(code = 404, message = "User not found or no followed journals")
    })
    @GetMapping("/user/{userId}/favorites")
    public Result<List<Journal>> getUserFavoriteJournals(@ApiParam(value = "ID of the user to get followed journals", required = true) @PathVariable Long userId) {
        QueryWrapper<Favorite> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).isNotNull("journal_id");
        List<Favorite> favorites = favoriteService.list(queryWrapper);
        List<Long> journalIds = favorites.stream().map(Favorite::getJournalId).collect(Collectors.toList());

        if (journalIds.isEmpty()) {
            return Result.ok("No followed journals found", Collections.emptyList());
        }

        List<Journal> journals = journalService.listByIds(journalIds);
        return Result.ok("Successfully retrieved followed journals", journals);
    }

    @ApiOperation(value = "Check if a user is following a journal", response = Result.class)
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully checked if user is following journal"),
            @ApiResponse(code = 404, message = "Journal not found")
    })
    @GetMapping("/{journalId}/is-followed/{userId}")
    public Result<Boolean> isUserFollowingJournal(
            @ApiParam(value = "ID of the journal to check", required = true) @PathVariable Long journalId,
            @ApiParam(value = "ID of the user to check", required = true) @PathVariable Long userId) {
        boolean isFollowing = favoriteService.isUserFollowingJournal(userId, journalId);
        return Result.ok("Successfully checked if user is following journal", isFollowing);
    }
}
